package com.moorror.cola.strategy.plugin.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.lang.jvm.annotation.*;
import com.intellij.lang.jvm.types.JvmReferenceType;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.impl.source.PsiClassReferenceType;

import javax.annotation.Nullable;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author jiyanghuang
 * @Date 2022/3/13 5:49 AM
 */
public class AnnotationTransferUtils {

    /**
     * 获取属性值
     *
     * @param attributeValue Psi属性
     * @return {Object | List}
     */
    @Nullable
    public static Object getAttributeValue(String fieldName, JvmAnnotationAttributeValue attributeValue) {
        if (attributeValue == null) {
            return null;
        }
        if (attributeValue instanceof JvmAnnotationConstantValue) {
            return ((JvmAnnotationConstantValue) attributeValue).getConstantValue();
        } else if (attributeValue instanceof JvmAnnotationEnumFieldValue) {
            JvmAnnotationEnumFieldValue fieldValue = (JvmAnnotationEnumFieldValue)attributeValue;
            try{
                // todo 不知道泛类型时，就不要使用泛类型
                Class enumClazz = Class.forName(fieldValue.getContainingClassName());
                return Enum.valueOf(enumClazz, fieldValue.getFieldName());
            }catch (Exception ex){
                return null;
            }
        } else if (attributeValue instanceof JvmAnnotationArrayValue) {
            List<JvmAnnotationAttributeValue> values = ((JvmAnnotationArrayValue) attributeValue).getValues();
            List<Object> list = new ArrayList<>(values.size());
            for (JvmAnnotationAttributeValue value : values) {
                Object o = getAttributeValue(fieldName, value);
                if (o != null) {
                    list.add(o);
                } else {
                    // 如果是jar包里的JvmAnnotationConstantValue则无法正常获取值
                    try {
                        Class<? extends JvmAnnotationAttributeValue> clazz = value.getClass();
                        Field myElement = clazz.getSuperclass().getDeclaredField("myElement");
                        myElement.setAccessible(true);
                        Object elObj = myElement.get(value);
                        if (elObj instanceof PsiExpression) {
                            PsiExpression expression = (PsiExpression) elObj;
                            list.add(expression.getText());
                        }
                    } catch (Exception ignore) {
                        ignore.printStackTrace();
                    }
                }
            }
            // 在未知泛型的情况下
            Object arrCopy = Array.newInstance(list.get(0).getClass(), list.size());
            for(int i = 0; i < list.size(); i++){
                Array.set(arrCopy, i, list.get(i));
            }
            return arrCopy;
        } else if (attributeValue instanceof JvmAnnotationClassValue) {
            JvmAnnotationClassValue value = ((JvmAnnotationClassValue) attributeValue);
            try{
                // todo 由于枚举可能类可能可以扩展，不被包含在插件包里，因此可能为空
                return Class.forName(value.getQualifiedName());
            }catch (Exception ex){
                // 通过生成一个代理类，目的是返回类名，但不一定能返回其他的方法
                JvmReferenceType jvmReferenceType = value.getClazz().getSuperClassType();
                if(jvmReferenceType instanceof PsiClassReferenceType){
                    PsiClassReferenceType psiClassReferenceType = (PsiClassReferenceType)jvmReferenceType;
                    PsiClass psiClass = psiClassReferenceType.resolve();
                    if(null != psiClass){
                        AnnotationClassNameHolderUtils.putValue(psiClass.getQualifiedName(), fieldName, value.getQualifiedName());
                        return null;
                    }
                }
                return null;
            }
        }else if(attributeValue instanceof JvmNestedAnnotationValue){
            JvmNestedAnnotationValue ann = (JvmNestedAnnotationValue)attributeValue;
            return AnnotationTransferUtils.annInsNest(ann.getValue().getQualifiedName(), (PsiAnnotation) ann.getValue());
        }
        return null;
    }

    /**
     * 获取类上的注解
     *
     * @param annClazz
     * @param psiClass
     * @param <T>
     * @return
     */
    public static <T extends Annotation> T annIns(Class<T> annClazz, PsiClass psiClass) {
        if (null == psiClass) {
            return null;
        }
        PsiAnnotation psiAnnotation = psiClass.getAnnotation(annClazz.getName());
        if (null == psiAnnotation) {
            return null;
        }
        AnnotationProxyJdk proxyJdk = new AnnotationProxyJdk(annClazz, psiAnnotation);
        return (T) Proxy.newProxyInstance(proxyJdk.getClass().getClassLoader(), new Class[]{annClazz}, proxyJdk);
    }

    /**
     * 获取方法上的注解
     *
     * @param annClazz
     * @param psiMethod
     * @param <T>
     * @return
     */
    public static <T extends Annotation> T annIns(Class<T> annClazz, PsiMethod psiMethod) {
        if (null == psiMethod) {
            return null;
        }
        PsiAnnotation psiAnnotation = psiMethod.getAnnotation(annClazz.getName());
        if (null == psiAnnotation) {
            return null;
        }
        AnnotationProxyJdk proxyJdk = new AnnotationProxyJdk(annClazz, psiAnnotation);
        return (T) Proxy.newProxyInstance(proxyJdk.getClass().getClassLoader(), new Class[]{annClazz}, proxyJdk);
    }


    /**
     * 获取类上的注解
     *
     * @param annClazz
     * @param psiClass
     * @param <T>
     * @return
     */
    public static <T extends Annotation> List<T> annInsRepeat(Class<T> annClazz, PsiClass psiClass) {
        if (null == psiClass) {
            return null;
        }
        PsiAnnotation[] psiAnnotations = psiClass.getAnnotations();
        if (null == psiAnnotations) {
            return null;
        }
        List<T> re = Lists.newArrayListWithExpectedSize(psiAnnotations.length);
        for (PsiAnnotation psiAnnotation : psiAnnotations) {
            if (!annClazz.getName().equals(psiAnnotation.getQualifiedName())) {
                continue;
            }
            AnnotationProxyJdk proxyJdk = new AnnotationProxyJdk(annClazz, psiAnnotation);
            T t = (T) Proxy.newProxyInstance(proxyJdk.getClass().getClassLoader(), new Class[]{annClazz}, proxyJdk);
            re.add(t);
        }
        return re;
    }

    /**
     * 如果是可以重复声明的注解，IDE 没有按 repeats 来获取，因此只通过遍历所有的注解
     * 通过名称匹配来过滤
     *
     * @param annClazz
     * @param psiMethod
     * @param <T>
     * @return
     */
    public static <T extends Annotation> List<T> annInsRepeat(Class<T> annClazz, PsiMethod psiMethod) {
        if (null == psiMethod) {
            return null;
        }
        PsiAnnotation[] psiAnnotations = psiMethod.getAnnotations();
        if (null == psiAnnotations) {
            return null;
        }
        List<T> re = Lists.newArrayListWithExpectedSize(psiAnnotations.length);
        for (PsiAnnotation psiAnnotation : psiAnnotations) {
            if (!annClazz.getName().equals(psiAnnotation.getQualifiedName())) {
                continue;
            }
            AnnotationProxyJdk proxyJdk = new AnnotationProxyJdk(annClazz, psiAnnotation);
            T t = (T) Proxy.newProxyInstance(proxyJdk.getClass().getClassLoader(), new Class[]{annClazz}, proxyJdk);
            re.add(t);
        }
        return re;
    }

    /**
     * 如果注解是嵌套
     * @param fClassName
     * @param psiAnnotation
     * @param <T>
     * @return
     */
    public static <T extends Annotation> T annInsNest(String fClassName, PsiAnnotation psiAnnotation) {
        try{
            Class<?> clazzT = Class.forName(fClassName);
            AnnotationProxyJdk proxyJdk = new AnnotationProxyJdk(clazzT, psiAnnotation);
            return (T) Proxy.newProxyInstance(proxyJdk.getClass().getClassLoader(), new Class[]{clazzT}, proxyJdk);
        }catch (Exception ex){
            return null;
        }
    }

    /**
     * 内部类处理转换逻辑
     *
     * @param <T>
     */
    private static class AnnotationProxyJdk<T extends Annotation> implements InvocationHandler {

        private Map<String, JvmAnnotationAttributeValue> nameValueMap;

        private Class<T> clazz;

        public AnnotationProxyJdk(Class<T> clazz, PsiAnnotation psiAnnotation) {
            this.clazz = clazz;
            nameValueMap = Maps.newHashMap();
            for (JvmAnnotationAttribute annotationAttribute : psiAnnotation.getAttributes()) {
                nameValueMap.put(annotationAttribute.getAttributeName(), annotationAttribute.getAttributeValue());
            }
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(this, args);
                }
                JvmAnnotationAttributeValue attributeValue = nameValueMap.get(method.getName());
                // 没有 key 表示使用了注解默认值，方法本身是可以返回默认值的
                if(null == attributeValue){
                    return method.getDefaultValue();
                }
                return AnnotationTransferUtils.getAttributeValue(method.getName(), attributeValue);
            } catch (InvocationTargetException ex) {
                throw ex.getTargetException();
            } catch (Exception ex) {
                throw ex;
            }
        }
    }
}
